/*
 * JSF Toolkit Component Framework
 * Copyright (C) 2007 Noah Sloan <iamnoah A-T gmail D0T com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 */
package com.jsftoolkit.gen.info;

import java.util.HashSet;
import java.util.Set;

/**
 * Base classes for information about a class to be generated.
 * 
 * @author noah
 */
public abstract class ClassInfo {

	private String _package;

	private boolean _abstract;

	private String className;

	protected Set<Class<?>> interfaces = new HashSet<Class<?>>();

	private Set<Class<?>> imports = new HashSet<Class<?>>();

	/**
	 * Initializes nothing.
	 * 
	 */
	public ClassInfo() {
		super();
	}

	/**
	 * Convenience constructor.
	 * 
	 * @param _package
	 *            the package for the generated class.
	 * @param className
	 *            the className of the generated class
	 * @param _abstract
	 *            is the class abstract?
	 * @param interfaces
	 *            interfaces this class should implement
	 */
	public ClassInfo(String _package, String className, boolean _abstract,
			Class<?>... interfaces) {
		this();
		this._package = _package;
		this._abstract = _abstract;
		this.className = className;
		for (Class<?> i : interfaces) {
			this.interfaces.add(i);
		}
	}

	/**
	 * A class being abstract has a special meaning in terms of code generation.
	 * If the class is abstract, it cannot be registered as the component (or
	 * TagHandler or Renderer), so the class name provided will NOT be
	 * interpreted as the class to be generated, rather className + 'Base' will
	 * be generated, but className will be the class that is registered.
	 * 
	 * @return true if the class should be abstract.
	 */
	public boolean isAbstract() {
		return _abstract;
	}

	public void setAbstract(boolean _abstract) {
		this._abstract = _abstract;
	}

	/**
	 * 
	 * @return the package the class should belong to.
	 */
	public String getPackage() {
		return _package;
	}

	public void setPackage(String _package) {
		if (_package != null) {
			this._package = _package;
		}
	}

	/**
	 * 
	 * @return the name of the class to generate. See
	 *         {@link #getActualClassName()} and {@link #isAbstract()}.
	 */
	public String getClassName() {
		return className == null ? null : className + nameSuffix();
	}

	protected String nameSuffix() {
		return (isAbstract() ? "Base" : "");
	}

	/**
	 * 
	 * @return the name of the class as it was specified.
	 */
	public String getActualClassName() {
		return className;
	}

	public void setClassName(String className) {
		if (className != null) {
			this.className = className;
		}
	}

	public String getActualCannonicalName() {
		return isAbstract() ? String.format("%s.%s", getPackage(),
				getActualClassName()) : getCanonicalClassName();
	}

	/**
	 * 
	 * @return the set of interfaces the class should implement
	 */
	public Set<Class<?>> getInterfaces() {
		return interfaces;
	}

	public void setInterfaces(Set<Class<?>> interfaces) {
		this.interfaces = interfaces;
	}

	/**
	 * The set of classes required by plain text code. Note that interfaces,
	 * property types and any other property that is a class constant will
	 * automatically be imported. This is only for imports required for custom
	 * code. e.g. if you specified a defaultValue for a property of 'new
	 * FooBar()', then class FooBar would need to be added to the imports.
	 * 
	 * @return
	 */
	public Set<Class<?>> getImports() {
		return imports;
	}

	public void setImports(Set<Class<?>> imports) {
		if (imports != null) {
			this.imports = imports;
		}
	}

	/**
	 * Adds an import.
	 * 
	 * @param o
	 */
	public void addImport(Class<?> o) {
		imports.add(o);
	}

	/**
	 * Method for Commons Digester. Normal code should use
	 * {@link #addImport(Class)}
	 * 
	 * @param className
	 * @throws ClassNotFoundException
	 */
	public void addImport(String className) throws ClassNotFoundException {
		addImport(Class.forName(className));
	}

	/**
	 * Adds an interface.
	 * 
	 * @param o
	 */
	public void addInterface(Class<?> o) {
		interfaces.add(o);
	}

	/**
	 * For CommonsDigester. Normal code should use {@link #addInterface(Class)}
	 * 
	 * @param className
	 * @throws ClassNotFoundException
	 */
	public void addInterface(String className) throws ClassNotFoundException {
		addInterface(Class.forName(className));
	}

	/**
	 * @return package+'.'+className
	 */
	public String getCanonicalClassName() {
		return String.format("%s.%s", getPackage(), getClassName());
	}

	@Override
	public String toString() {
		return getActualCannonicalName();
	}
}
